//*****************************************************************************
// * (C) Copyright 2008 Daniel L Williams
// ****************************************************************************/

using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Reflection;

namespace dlw.S3Examples
{
    /// <summary>
    /// Summary description for TestMain.
    /// </summary>
    public class TestMain
    {
        private static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        [STAThread]
        static int Main(string[] args)
        {
            DateTime start = DateTime.Now;

            int i = 0;
            try
            {
                log.Info("\n\n\t***Starting Test");
                string allArgs = "";
                foreach (string arg in args)
                {
                    allArgs += arg + " ";
                }
                allArgs = allArgs.Trim();
                log.Info("Args: " + allArgs);
                i = RunTest(args);
            }
            catch (Exception ex)
            {
                log.Error("Error: " + ex.ToString());
                log.Error("Stack: " + ex.StackTrace.ToString());
            }
            log.Info("RunTime: " + ((TimeSpan)(DateTime.Now - start)).TotalSeconds.ToString());
            log.Info("Result: " + i.ToString());
            return i;
        }

        static int RunTest(string[] args)
        {
            System.Threading.Thread.CurrentThread.Name = "dlw.TestMain";
            // get a list of all classes that have the attribute "TestClass"
            string testClass = "";
            string method = "";
            string[] subargs = null;
            if (args.Length > 2)
            {
                subargs = new string[args.Length - 2];
                for (int n = 2; n < args.Length; n++)
                {
                    subargs[n - 2] = args[n];
                }
            }
            if (args.Length > 1)
            {
                method = args[1];
            }
            if (args.Length > 0)
            {
                testClass = args[0];
            }

            if (args.Length == 0)
            {
                Help();
                return 1;
            }
            if (args.Length == 1)
            {
                if (testClass.ToUpper() == "-H"
                    || testClass.ToUpper() == "-HELP"
                    || testClass.ToUpper() == "H"
                    || testClass.ToUpper() == "HELP")
                {
                    Help();
                    return 1;
                }
                else
                {
                    // list all test classes and methods
                }
            }
            else if (args.Length > 1)
            {
                // invoke the requested method
            }

            Assembly theAss = Assembly.GetEntryAssembly();
            Hashtable testTypes = new Hashtable();
            Type[] types = theAss.GetTypes();

            MethodInfo callMethod = null;
            Type callType = null;
            foreach (Type typ in types)
            {
                Object[] att = typ.GetCustomAttributes(typeof(TestClassAttribute), false);
                if (att.Length < 1) continue; // this is not a test class

                // case-insensitive comparison of the test class name
                if (typ.Name.ToUpper() == testClass.ToUpper())
                {
                    callType = typ; // this is the type requested 
                }
                // put all the types into our list
                testTypes.Add(typ.Name.ToUpper(), typ);

                foreach (MethodInfo mi in typ.GetMethods())
                {
                    Object[] testMethods = mi.GetCustomAttributes(typeof(TestMethodAttribute), false);
                    if (testMethods.Length > 0)
                    {
                        if (mi.Name.ToUpper() == method.ToUpper()
                            && typ == callType)	// must be the method of this type!
                        {
                            int nargs = mi.GetParameters().Length;
                            if (nargs == 0 && subargs == null)
                            {
                                callMethod = mi;
                            }
                            else if (subargs != null && subargs.Length == nargs)
                            {
                                callMethod = mi;
                            }
                        }
                    }
                }
            }   // iterate over all types in the assembly
            if (args.Length == 1)
            {
                if (callType == null)
                {
                    log.Error("Test Class not found: " + testClass);
                    return -1;
                }
                log.Info("Test Class: " + testClass);
                log.Info("Methods: ");
                foreach (MethodInfo mi in callType.GetMethods())
                {
                    string message = "";
                    foreach (ParameterInfo pi in mi.GetParameters())
                    {
                        message += " " + pi.Name;
                    }
                    log.Info("\tMethod: " + mi.Name + message);
                }
            }
            else if (args.Length > 1)
            {
                if (callType == null || callMethod == null)
                {
                    log.Info("Test Class and method not found: " + testClass + " : " + method + " Args: " + (args.Length - 2).ToString());
                    return -1;
                }
                Invoke(callType, callMethod, subargs);
            }


            return 0;
        }	//static int Main(string[] args)

        static protected void Invoke(Type testClass, MemberInfo method, string[] args)
        {
            // invoke this static, void method
            DateTime start = DateTime.Now;
            object result;
            object baseObject = Activator.CreateInstance(testClass);
            result = testClass.InvokeMember(method.Name, BindingFlags.Default | BindingFlags.InvokeMethod
                , null, baseObject, args);
            log.Info("RunTime: " + ((TimeSpan)(DateTime.Now - start)).TotalSeconds.ToString());
            return;
        }

        static protected void Help()
        {
            // tbd: follow some nice standard, winging it for now
            log.Error("\nusage: Test CLASS METHOD\n");
            Assembly theAss = Assembly.GetEntryAssembly();
            Type[] types = theAss.GetTypes();
            foreach (Type typ in types)
            {
                Object[] att = typ.GetCustomAttributes(typeof(TestClassAttribute), false);
                if (att.Length < 1) continue; // this is not a test class
                // we want this one
                log.Info("Class: " + typ.Name);

                foreach (MethodInfo mi in typ.GetMethods())
                {
                    Object[] testMethods = mi.GetCustomAttributes(typeof(TestMethodAttribute), false);
                    if (testMethods.Length > 0)
                    {
                        string message = "";
                        foreach (ParameterInfo pi in mi.GetParameters())
                        {
                            message += " " + pi.Name;
                        }
                        log.Info("\tMethod: " + mi.Name + message);
                    }
                }
            }
        }

    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public sealed class TestClassAttribute : Attribute
    {
    }
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class TestMethodAttribute : Attribute
    {
    }

}







